package org.sundatagroup.aurora.show.controller;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.sundatagroup.aurora.core.controller.IJsonResultController;
import org.sundatagroup.aurora.core.service.querier.QuerierCenter;
import org.sundatagroup.aurora.show.config.IShow;
import org.sundatagroup.aurora.show.entity.status.PlayingStatus;
import org.sundatagroup.aurora.show.entity.status.ReadyStatus;
import org.sundatagroup.aurora.show.entity.status.WaitingStatus;
import org.sundatagroup.aurora.show.service.ScreenStatusService;
import org.sundatagroup.aurora.show.service.SidDispatcher;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;

/**
 * <p>处理展示屏状态的服务器终端
 * </p>
 * 
 * @version 0.1.1
 * <br>实现了部分逻辑
 * 
 * @author Zdream
 * @date 2018-04-12
 * @since 0.1
 */
@Component
public class DisplayStatusEndpoint extends TextWebSocketHandler implements IJsonResultController,IShow {
	
	@Autowired
	ScreenStatusService statusQuerier;

	@Autowired
	SidDispatcher sider;

	@Autowired
	QuerierCenter qrCenter;

	@Autowired
	DisplaySocketController sender;

	private final static Logger LOGGER = LoggerFactory.getLogger(DisplayStatusEndpoint.class);
	private static JsonParser parser = new JsonParser();
	
	@Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		String sid = scanSid(session);
		
		if (sid == null) {
			sid = sider.createSid(); // 新分配的 sid
			LOGGER.info("展示区连接屏幕 " + session.getId() + ", 新分配 ID #" + sid);
			
			// 发送分配 sid 的数据
			this.sendDispatcherSid(session, sid);
		} else {
			LOGGER.info("展示区连接屏幕 " + session.getId() + ", #" + sid);
		}
		
		statusQuerier.open(sid, session);
    }

	@Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		String sid = statusQuerier.searchSidByWebSocket(session);
		
		statusQuerier.close(sid, session);
    	LOGGER.info("展示区关闭屏幕 " + session.getId());
    }

    /**
     * 收到客户端消息后调用的方法
     * @throws IOException 
     */
	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		String sid = statusQuerier.searchSidByWebSocket(session);
		try {
			JsonObject o = parser.parse(message.getPayload()).getAsJsonObject();
			String type = o.get("type").getAsString();

			switch (type) {
			case "/status":
				handleStatusSyncMessage(sid, o.get("data").getAsJsonObject());
				break;
			default:
				if (type.startsWith("/query/")){
					handleQuery(session, type, o.get("data").getAsJsonObject());
				}
				break;
			}
		} catch (JsonSyntaxException e) {
			LOGGER.error("展示屏 #%s 消息: %s 无法解析", sid, message);
		}
	}
	
	/**
	 * 收到状态更新的消息
	 * @throws IOException 
	 */
	private void handleStatusSyncMessage(String sid, JsonObject data)
			throws IOException {
		String status = null;
		try {
			status = data.get("status").getAsString();
		} catch (Exception e) {
			e.printStackTrace();
			LOGGER.error("展示区收到的消息出现错误: " + data.toString());
			return;
		}
		switch (status) {
		case "ready":
			handleReadyStatusMessage(sid, data);
			break;
		case "play-begin-one-scene":
			handlePlayStatusMessage(sid, data);
			break;
		case "play-complete":
			handleCompleteStatusMessage(sid, data);
			break;
		default: {
			LOGGER.info(String.format("展示屏 #%s 状态: %s @lql 请做其它处理: 位置:", sid, status));
			LOGGER.warn(Thread.currentThread().getStackTrace()[1].toString());
			LOGGER.warn("还有如果你写的东西的格式和上面不一样, 我有权认为你什么都没做");
		} break;
		}
	}
	
	/**
	 * 处理 ready 状态事件
	 * @param session
	 * @param data
	 */
	private void handleReadyStatusMessage(String sid, JsonObject data) throws IOException {
		ReadyStatus status = new ReadyStatus();
		status.setSid(sid);
		
		// pid 允许没有
		JsonElement je = data.get("pid");
		if (je != null && !je.isJsonNull()) {
			status.setPid(je.getAsInt());
		}
		
		statusQuerier.updateStatus(sid, status);
	}
	
	/**
	 * 处理 play-begin-one-scene 状态事件
	 * @param session
	 * @param data
	 * @throws IOException 
	 */
	private void handlePlayStatusMessage(String sid, JsonObject data) throws IOException {
		PlayingStatus status = new PlayingStatus();
		
		JsonObject mo = data.getAsJsonObject("module");
		status.setModuleIndex(mo.get("index").getAsInt());
		status.setModuleName(mo.get("name").getAsString());
		status.setModuleLength(mo.get("length").getAsInt());
		
    	JsonObject so = data.getAsJsonObject("scene");
		status.setSceneIndex(so.get("index").getAsInt());
		status.setSceneLength(so.get("length").getAsInt());
		statusQuerier.updateStatus(sid, status);
	}
	
	/**
	 * 处理 play-complete 状态事件
	 * @param session
	 * @param data
	 * @throws IOException
	 */
	private void handleCompleteStatusMessage(String sid, JsonObject data) throws IOException {
		// 播放结束转入等待状态
		WaitingStatus status = new WaitingStatus(sid);
		status.setSid(sid);
		
		statusQuerier.updateStatus(sid, status);
	}
	
	/**
	 * 发布 sid 的消息
	 * TODO 如果看到，请把该方法放到其它 Controller 里面
	 * @param session
	 * @param sid
	 */
	private void sendDispatcherSid(WebSocketSession session, String sid) {
		JsonObject jo = rawResultJsonObject(true);
		jo.addProperty("type", "/show/sidDispatcher");
		
		JsonObject data = new JsonObject();
		data.addProperty("sid", sid);
		jo.add("data", data);
		
		this.sendMessage(session, jo.toString());
	}
	
	/**
	 * 发送消息给订阅的客户端
	 * @param message
	 * @throws IOException
	 */
	public void sendMessage(WebSocketSession session, String message) {
		try {
			session.sendMessage(new TextMessage(message));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
    
	/**
	 * 从 session URI 中获取屏幕 id 号.
	 * @param session
	 * @return
	 *   屏幕 id 号, 一般是 UUID.
	 *   <br>如果在等待页, 则返回 null
	 * @throws IllegalArgumentException
	 */
    private String scanSid(WebSocketSession session) throws IllegalArgumentException {
    	String uri = session.getUri().toString();
    	if ("/aurora/wait".equals(uri)) {
    		return null;
    	}
    	
    	try {
			return uri.substring("/aurora/screen/".length());
		} catch (Exception e) {
			throw new IllegalArgumentException("无法在 URI: " + session.getUri() + " 中取得 sid 信息.", e);
		}
    }

	/**
	 * 处理通用查询
	 * @param session
	 * @param type
	 * @param data
	 */
	public void handleQuery(WebSocketSession session, String type, JsonObject data) {
		JsonElement je = qrCenter.query(ID, type, data);

		if (je != null) {
			sender.queryReturn(session, type.replace("query", "query-return"), je);
		} else {
			sender.queryReturnFall(session, type.replace("query", "query-return"));
		}
	}
}


